#include "st_server.h"
#include "sys_socket.h"
#include "st_sock_holder.h"
#include "st_waiter_thread.h"

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

STServer::STServer(unsigned int port)
{
    m_serverSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_serverSock != -1) {
        // bind
        sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        if ((-1 == bind(m_serverSock, (sockaddr*)&addr, sizeof(addr))) ||
            (-1 == listen(m_serverSock, 10))) {
            closesocket(m_serverSock);
            m_serverSock = -1;
            throw "Bind or listen failed";
        }
    }
    // The code is ugly. Making it a state machine will be better?
    m_msgSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    socklen_t addrLen = sizeof(m_msgSockAddr);
	memset(&m_msgSockAddr, 0, sizeof(m_msgSockAddr));
	m_msgSockAddr.sin_family = AF_INET;
#ifdef _WIN32
    m_msgSockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
	m_msgSockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
#endif
    getsockname(m_msgSock, (sockaddr*)&m_msgSockAddr, &addrLen);
    bind(m_msgSock, (sockaddr*)&m_msgSockAddr, addrLen);
    getsockname(m_msgSock, (sockaddr*)&m_msgSockAddr, &addrLen);

    m_waiterThread = new STWaiterThread();
}

STServer::~STServer()
{
    stop();
    closesocket(m_serverSock);
    closesocket(m_msgSock);
}

void STServer::run()
{
    // first start waiter thread
    m_waiterThread->start();

    while (true)
    {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(m_serverSock, &fds);
        FD_SET(m_msgSock, &fds);

        int rc = select((m_serverSock > m_msgSock? m_serverSock: m_msgSock) + 1,
                        &fds, 0, 0, 0);
        if (rc < 0)
        {
            cerr << "STServer::run: select failed" << endl;
            break;
        }
        if (FD_ISSET(m_msgSock, &fds))
        {
            if (!handleMsg())
                break;
        }
        if (FD_ISSET(m_serverSock, &fds))
        {
            int sock = accept(m_serverSock, 0, 0);
            if (sock != -1)
            {
                STSockHolder* sockHolder = newSockHolder(sock);
                m_waiterThread->addSockHolder(sockHolder);
            }
        }
    }
    // stop waiter thread
    m_waiterThread->stop();
}

void STServer::stop()
{
    sendMsg("", 0);
    join();
}

bool STServer::handleMsg()
{
    char buffer[1000];
    int bufLen = sizeof(buffer);
    int rc = recv(m_msgSock, buffer, bufLen, 0);
    bool ret = true;

    if (rc <= 0)
    {
        // error or no msg, break
        ret = false;
    }
    else
    {
        // handle the message
        cout << "STServer::handleMsg: got " << rc << " bytes" << endl;
    }
    return ret;
}

void STServer::sendMsg(const void* msg, unsigned int msgLen)
{
    sendto(m_msgSock, (const char*)msg, msgLen, 0, (sockaddr*)&m_msgSockAddr, sizeof(m_msgSockAddr));
}

/**
 * create a sock holder from a socket.
 * It's better to create through a factory? Keep it simple right now.
 */
STSockHolder* STServer::newSockHolder(int sock)
{
    return new STSockHolder(sock);
}


